﻿using System.Diagnostics;
using HK.Core.Logs.Data;
using HK.Core.Logs.Protocol;
using HK.Core.Pool;
using HK.Core.Utils;
using UDebug = UnityEngine.Debug;
using HLogLevel = HK.Core.Logs.Protocol.LogLevel;

namespace HK.Core.Logs
{
    /// <summary>
    /// 日志
    /// </summary>
    public static class Logger
    {
        static Logger()
        {
            // 初始化日志数据缓冲池
            ObjectPool<LogData>.Generate(9999, true, 0.2f);

            LogLevel = LogLevel.All;
        }
        
        /// <summary>
        /// 日志等级
        /// </summary>
        public static bool Enable { get; private set; } = false;

        /// <summary>
        /// 日志等级
        /// </summary>
        public static HLogLevel LogLevel { get; private set; } = HLogLevel.Off;
        
        /// <summary>
        /// 日志输出标志位
        /// <para>* 将日志输出到日志文件</para>
        /// </summary>
        public static bool LogOutput { get; private set; } = false;

        /// <summary>
        /// 输出路径
        /// </summary>
        public static string OutputDir { get; private set; } = null;

#region Data

        /// <summary>
        /// 日志No
        /// </summary>
        private static long LogNo { get; set; } = 0L;

        /// <summary>
        /// 生成日志数据
        /// </summary>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLog">日志</param>
        /// <param name="iIsBuild">打包日志标志位</param>
        /// <returns>日志数据</returns>
        private static ILogData GenerateLogData(HLogLevel iLevel, string iLog, bool iIsBuild = false)
        {
            var logData = ObjectPool<LogData>.Pop();
            if (null == logData) return null;
            
            ++LogNo;
            logData.Reset(LogNo, iLevel, iLog, iIsBuild);
            if (logData.Valid) return logData;
            
            ObjectPool<LogData>.Recycle(logData);
            return null;
        }

#endregion

#region Normal

        /// <summary>
        /// 信息日志(测试日志).
        /// </summary>
        /// <param name="iLog">日志.</param>
        [Conditional("HK_LOG_TEST")]
        public static void Test(string iLog)
        {
            OutputLog(HLogLevel.Test, iLog);
        }
        
        /// <summary>
        /// 信息日志(默认：运行日志).
        /// </summary>
        /// <param name="iLog">日志.</param>
        [Conditional("HK_LOG_INFO")]
        public static void Info(string iLog)
        {
            OutputLog(HLogLevel.RInfo, iLog);
        }

        /// <summary>
        /// 警告日志.
        /// </summary>
        /// <param name="iLog">日志.</param>
        [Conditional("HK_LOG_WARNING")]
        public static void Warning(string iLog)
        {
            OutputLog(HLogLevel.Warning, iLog);
        }

        /// <summary>
        /// 信息:逻辑(LI).
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void LInfo(string iLog)
        {
            OutputLog(HLogLevel.LInfo, iLog);
        }

        /// <summary>
        /// 错误日志.
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void Error(string iLog)
        {
            OutputLog(HLogLevel.Error, iLog);
        }

        /// <summary>
        /// 致命日志.
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void Fatal(string iLog)
        {
            OutputLog(HLogLevel.Fatal, iLog);
        }

#endregion

#region Build

        /// <summary>
        /// 信息日志(打包日志 - B).
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void BuildLog(string iLog)
        {
            OutputLog(HLogLevel.RInfo, iLog, true);
        }
                
        /// <summary>
        /// 信息日志(打包警告日志 - BW).
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void BuildWarningLog(string iLog)
        {
            OutputLog(HLogLevel.Warning, iLog, true);
        }
                
        /// <summary>
        /// 信息日志(打包错误日志 - BE).
        /// </summary>
        /// <param name="iLog">日志.</param>
        public static void BuildErrorLog(string iLog)
        {
            OutputLog(HLogLevel.Error, iLog, true);
        }

#endregion

        /// <summary>
        /// 输出日志
        /// </summary>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLog">日志</param>
        /// <param name="iIsBuild">打包日志标志位</param>
        private static void OutputLog(HLogLevel iLevel, string iLog, bool iIsBuild = false)
        {
            if (LogLevel > iLevel) return;
            OutputLogDetail(iLevel, iLog, iIsBuild);
        }

#if FUNCTION_TESTS

        /// <summary>
        /// 时间消耗计数器
        /// </summary>
        private static Stopwatch _costTimer = new Stopwatch();
        
#endif
        
        /// <summary>
        /// 输出日志详细内容
        /// </summary>
        /// <param name="iLevel">日志等级</param>
        /// <param name="iLog">日志</param>
        /// <param name="iIsBuild">打包日志标志位</param>
        private static void OutputLogDetail(HLogLevel iLevel, string iLog, bool iIsBuild = false)
        {
            if(string.IsNullOrEmpty(iLog)) return;
            var logData = GenerateLogData(iLevel, iLog, iIsBuild);
            if(null == logData || !logData.Valid) return;
            
            var costTime = 0L;
#if FUNCTION_TESTS
            
            _costTimer.Stop();
            costTime = _costTimer.ElapsedMilliseconds;
            _costTimer.Restart();
            
#endif            
            var curTime = UtilsTime.GetCurDateTimeTicks();
            var curTimeTxt = UtilsTime.ConvertByFormat(curTime, UtilsTime.DateFormat.HhMmSsMs);
            var logs = LogData.GenerateLog(
                logData.No, logData.Level, logData.Log, curTimeTxt, 
                costTime, out var oDisplayLog, logData.IsBuild);
            if (!string.IsNullOrEmpty(oDisplayLog))
            {
                switch (logData.Level)
                {
                    case HLogLevel.Error:
                    case HLogLevel.Fatal:
                        UDebug.LogError(oDisplayLog);
                        break;
                    case HLogLevel.Warning:
                        UDebug.LogWarning(oDisplayLog);
                        break;
                    default:
                        UDebug.Log(oDisplayLog);
                        break;
                }
            }
            
        }
        
        /// <summary>
        /// 释放
        /// </summary>
        public static void Dispose()
        {
            ObjectPool<LogData>.Clear();
        }
        
    }
    
}
